/************************************************************************
 *				SRLOS Team CopyRight
 *@file		mt_mutex.h
 *@author	bloceanc
 *@date		7/21/2010
 *@note		kernel mutex
 ************************************************************************/
 
#include "commdef.h"
#include "task.h"
#include "mt_mutex.h"

extern k_thread *g_current_thread;

extern void comm_global_critical_section_enter(void);
extern void comm_global_critical_section_leave(void);

extern void task_thread_insert(k_thread *thread, unsigned int state);
extern void task_schedule(k_thread *thread);

extern long long get_tick_count(void);

void mt_initialize(void)
{

}

/*
 *	initialize mutex, depth set to 0
 * 		thread	if NULL using current thread
 */
void mt_mutex_initset(k_mutex *mutex)
{
	if(mutex == NULL)
		return;	// faild
	mutex->depth = 0;
	mutex->owner= NULL;
	mutex->wait_list = NULL;
	mutex->next = NULL;
	mutex->prev = NULL;
}

/*
 * 	add thread to waitting list
 * 	we need global lock (some one add some one delete or add)
 */
static void s_mt_mutex_thread_sleep(k_mutex *mutex, k_thread *thread)
{
	comm_global_critical_section_enter();
	thread->mutex_wait = mutex;
	thread->wait_next = mutex->wait_list;
	if(mutex->wait_list)
		mutex->wait_list->wait_prev = thread;
	thread->wait_prev = NULL;
	mutex->wait_list = thread;
	comm_global_critical_section_leave();
}

/*
 * wake up a thread from waitting list
 * we need global lock (some one add some one delete)
 */
static k_thread *s_mt_mutex_thread_wakeup(k_mutex *mutex)
{
	k_thread *thread;
	comm_global_critical_section_enter();
	thread = mutex->wait_list;
	if(thread)
	{
		mutex->wait_list = mutex->wait_list->wait_next;
		if(mutex->wait_list)
			mutex->wait_list->wait_prev = NULL;
		mutex->depth = thread->mutex_depth;
		mutex->owner = thread;
		thread->mutex_wait = NULL;
		thread->wait_next = NULL;
		thread->wait_prev = NULL;
		thread->mutex_depth = 0;
		thread->mutex_overtime = 0;
	}
	else
	{
		// there no thread using this mutex
		// initialize mutex for new lock
		mutex->depth = 0;
		mutex->owner = NULL;
	}
	comm_global_critical_section_leave();
	return thread;
}

/*
 * Remove thread from mutex waitting queue
 *  NOTE:this function is just used to do simple operation.can't do anyting more.
 */
int mt_mutex_wait_remove_thread(k_mutex *mutex, k_thread *thread)
{
	if(thread == NULL || mutex == NULL )
		return;
	if(thread->wait_next != NULL && thread->wait_prev != NULL)
	{
		thread->wait_next->wait_prev = thread->wait_prev;
		thread->wait_prev->wait_next = thread->wait_next;
	}
	if(thread->wait_next == NULL && thread->wait_prev == NULL)
	{
		mutex->wait_list = NULL;
	}
	if(thread->wait_next != NULL && thread->wait_prev == NULL)
	{
		mutex->wait_list = mutex->wait_list->next;
		mutex->wait_list->wait_prev = NULL;
	}
	if(thread->wait_next == NULL && thread->wait_prev != NULL)
	{
		// the last one
		thread->wait_prev->wait_next = NULL;
	}
	thread->wait_next = NULL;
	thread->wait_prev = NULL;
}

/*
 *	mutex timer process
 * 		when some threads waitting mutex using overtime.this function will wake up the thread
 * when it hasn't get lock and overtimed!
 * this function will scan the thread list
 * 
 * NOTE:this method will called by scheduler when schedule occurred, process willed checked
 */
void mt_mutex_overtime_handle(k_process *process)
{
	k_thread *cur;
	long long time = get_tick_count();

	cur = process->thread_waitting;
	while(cur)
	{
		if(cur->mutex_overtime > 0 && cur->mutex_overtime < time)
		{
			mt_mutex_wait_remove_thread(cur->mutex_wait, cur);
			cur->mutex_wait = NULL;
			cur->wait_next = NULL;
			cur->wait_prev = NULL;
			cur->mutex_depth = 0;
			cur->mutex_overtime = 0;
			// insert to runnable list
			task_thread_insert(cur, TASK_THREAD_STATE_RUNABLE);
		}	
		cur = cur->next;
	}
}


/*
 *	lock mutex
 * 		mutex	mutex wanted to lock
 * 		time	overtime in millionseconds, -1 means wait forever
 * 	return:		if get the lock, return 1
 * 				else return 0. this will be used when overtime = -1
 */
int mt_mutex_lock(k_mutex *mutex, long long time)
{
	if(mutex == NULL)
		return 0;
	
	// global lock!
	comm_global_critical_section_enter();
	if(mutex->depth == 0)
	{
		mutex->depth++;
		mutex->owner = g_current_thread;
		comm_global_critical_section_leave();
		return 1;
	}
	
	// mutex ower is current thread
	if(mutex->owner == g_current_thread)
	{
		// just inc depth
		mutex->depth++;
		comm_global_critical_section_leave();
		return 1;
	}
	
	// if time == 0, don't make thread wait!
	if(time == 0)
	{
		comm_global_critical_section_leave();
		return 0;
	}
	
	/*
	 *	mutex ower is not current thread
	 * insert it into mutex waiting thread and watting current thread
	 */
	g_current_thread->mutex_depth = 1;	/* this is the first lock of current thread */
	
	if(time < 0)
		g_current_thread->mutex_overtime = -1;
	else
	{
		g_current_thread->mutex_overtime = time + get_tick_count();
	}
	
	s_mt_mutex_thread_sleep(mutex,g_current_thread);	// insert into waitting list
	
	// set thread state and schedule if nessassery
	task_thread_insert(g_current_thread, TASK_THREAD_STATE_WAITTING);
	
	// must unlock here, otherwise there will be faital error!!!!
	comm_global_critical_section_leave();
	
	// we must schedule task after insert thread to waitting list.
	task_schedule(g_current_thread);
	
	// when thread wakeup, it will continue run here, then it is current thread!
	if(g_current_thread == mutex->owner)
	{
		// thread has got the lock!
		return 1;
	}
	
	// thread didn't get lock.
	return 0;
}

/*
 *	unlock mutex
 */
void mt_mutex_unlock(k_mutex *mutex)
{
	k_thread *thread;
	
	if(mutex->owner != g_current_thread || mutex->depth == 0)
	{
		// system error!
		return;
	}
	
	// 我们使用这个小技巧避免使用全局锁!
	if(mutex->depth == 1)
	{
		thread = s_mt_mutex_thread_wakeup(mutex);
		if(thread == NULL)
		{
			// there no more thread waitting using mutex
			return;
		}
		
		// insert thread to runnable list
		task_thread_insert(thread, TASK_THREAD_STATE_RUNABLE);
	}
	else
	{
		mutex->depth--;
	}
}

